package service

import (
	"context"
	"errors"
	"github.com/qiniu/api.v7/v7/auth/qbox"
	"github.com/qiniu/api.v7/v7/storage"
	"gorm.io/gorm"
	"math/rand"
	"mime/multipart"
	"path"
	"shop-bean/constant"
	"shop-bean/model"
	"shop-common/library/variables"
	"shop-common/utils/queryWrapper"
	"shop-service/repository"
	"strconv"
	"time"
)

type IAttachFileService interface {
	FindExpireFiles(context.Context) ([]string, error)
	Upload(context.Context, multipart.File, *multipart.FileHeader) (*model.AttachFile, bool, error)
	Create(context.Context, *model.AttachFile) (*model.AttachFile, error)
	DeleteBatchExpire(context.Context, []string) error
	CheckAttachFileExist(context.Context, *model.AttachFile) error
	UpdateBatchAttachFileStatus(context.Context, *gorm.DB, []uint, int) error
	FindOneFile(context.Context, *queryWrapper.WherePair) (*model.AttachFile, error)
}

type AttachFileService struct {
	db         *gorm.DB
	repository repository.IAttachFileRepository
}

func NewAttachFileService(repository repository.IAttachFileRepository) IAttachFileService {
	return &AttachFileService{variables.GormDB(), repository}
}

const (
	bucket_name = "shop-attach"
	accessKey   = "9WmkCSl4K2MN9FvQNpqZOwB17ndnU5RU_OqeE_x2"
	secretKey   = "Q7KGbr_Vo-pa9udsegmVff04-BaZLOkv8GwFlZN0"
)

func (s *AttachFileService) FindExpireFiles(ctx context.Context) ([]string, error) {
	list, err := s.repository.FindExpireFile(s.db)
	if err != nil {
		variables.Logger.Errorf(ctx, "find expire attacheFile failed: err %", err)
		return nil, err
	}

	var expireFileAll []string
	for _, file := range list {
		expireFileAll = append(expireFileAll, file.FileName)
	}

	return expireFileAll, nil
}

func (s *AttachFileService) Create(ctx context.Context, file *model.AttachFile) (*model.AttachFile, error) {
	err := s.repository.Create(s.db, file)
	if err != nil {
		variables.Logger.Errorf(ctx, "create attacheFile failed: err %v", err)
		return nil, errors.New("附件记录失败")
	}
	return *&file, nil
}

func (s *AttachFileService) DeleteBatchExpire(ctx context.Context, fileNames []string) error {
	mac := qbox.NewMac(accessKey, secretKey)
	cfg := storage.Config{
		Zone:          &storage.ZoneHuanan,
		UseCdnDomains: false,
		UseHTTPS:      false,
	}
	bucketManager := storage.NewBucketManager(mac, &cfg)

	deleteOps := make([]string, 0, len(fileNames))
	for _, fileName := range fileNames {
		deleteOps = append(deleteOps, storage.URIDelete(bucket_name, fileName))
	}

	_, err := bucketManager.Batch(deleteOps)

	if err != nil {
		// 遇到错误
		if _, ok := err.(*storage.ErrorInfo); ok {
			variables.Logger.Errorf(ctx, "bucket delete expire attachFile failed: err %", err)
			return errors.New("oss删除失败")
		}
	}

	err = s.repository.DeleteBatch(s.db, fileNames)
	if err != nil {
		variables.Logger.Errorf(ctx, "deleteBatch expire attachFile failed: err %, fileName %s", err, fileNames)
		return err
	}

	return nil
}

func (s *AttachFileService) CheckAttachFileExist(ctx context.Context, file *model.AttachFile) error {
	existCount, err := s.repository.CheckAttachFileExist(s.db, file)
	if err != nil {
		variables.Logger.Errorf(ctx, "check file failed: err %v", err)
		return errors.New("查询附件失败")
	} else if existCount > 0 {
		return errors.New("附件已存在")
	}

	return nil
}

func (s *AttachFileService) Upload(ctx context.Context, file multipart.File, header *multipart.FileHeader) (*model.AttachFile, bool, error) {
	putPolicy := storage.PutPolicy{
		Scope:   bucket_name,
		Expires: 120,
	}

	mac := qbox.NewMac(accessKey, secretKey)
	upToken := putPolicy.UploadToken(mac)

	cfg := storage.Config{
		Zone:          &storage.ZoneHuanan,
		UseCdnDomains: false,
		UseHTTPS:      false,
	}

	putExtra := storage.PutExtra{}
	formUploader := storage.NewFormUploader(&cfg)
	ret := storage.PutRet{}

	// 文件名
	fileKey := strconv.FormatInt(time.Now().Unix(), 10) + strconv.Itoa(rand.Intn(999999-100000)+100000) + path.Ext(header.Filename)
	variables.Logger.Info(ctx, "bucket upload attacheFile: %s", header.Filename)
	err := formUploader.Put(context.Background(), &ret, upToken, fileKey, file, header.Size, &putExtra)
	if err != nil && err.Error() == "file exists" {
		return &model.AttachFile{
			FileFormerName: header.Filename,
			FileName:       fileKey,
			FileSize:       int(header.Size),
			Hash:           ret.Hash,
			Bucket:         bucket_name,
			Status:         constant.FILE_FINITUDE, // 新增
			ExpireAt:       time.Now().AddDate(0, 0, +1),
		}, true, nil
	}

	if err != nil && err.Error() != "file exists" {
		variables.Logger.Errorf(ctx, "bucket upload attacheFile failed: err %v", err)
		return nil, false, errors.New("附件上传失败")
	}

	return &model.AttachFile{
		FileFormerName: header.Filename,
		FileName:       ret.Key,
		FileSize:       int(header.Size),
		Hash:           ret.Hash,
		Bucket:         bucket_name,
		Status:         constant.FILE_FINITUDE, // 24h later corn delete attach
		ExpireAt:       time.Now().AddDate(0, 0, +1),
	}, false, nil
}

func (s *AttachFileService) UpdateBatchAttachFileStatus(ctx context.Context, db *gorm.DB, fileIds []uint, state int) error {
	if err := s.repository.UpdateBatchStatus(db, fileIds, state); err != nil {
		variables.Logger.Errorf(ctx, "updateBatch attacheFile status failed:  %v, files: %s, state: %d", err, fileIds, state)
		return err
	}
	return nil
}

func (s *AttachFileService) FindOneFile(ctx context.Context, wp *queryWrapper.WherePair) (*model.AttachFile, error) {
	file, err := s.repository.FindOneByNameFile(s.db, wp)
	if err != nil {
		variables.Logger.Errorf(ctx, "findOne attacheFile filed: err %v, pair %+v", err, wp)
		return nil, err
	}

	return file, nil
}
